1. Technical Field
The present invention relates generally to the field of Computer-Aided Software Engineering (CASE), and more specifically to technology for managing and automating the development of large-scale software applications.
2. Description of the Related Art
“Software engineering” is the practice of software development as an industrial discipline. The goal of software engineering is to produce high-quality, maintainable software through an efficient use of development resources. As a field of study, software engineering may be thought of as the intersection between computer science and industrial engineering, as software engineering is as much concerned with the process of creating and maintaining software as it is with the product itself. The “software lifecycle” is a general term that is used to denote the overall process of creating, using, and maintaining a software product.
Although software engineering research often involves the use of scientific methods and mathematical models to attempt to understand the software lifecycle, the practice of software engineering is best regarded as an art. As an art, current software engineering practice is characterized by a significant number of competing software development methodologies and techniques, which, with a bit of imagination, could be analogized to different movements or styles in the visual arts and in music.
Some of these methodologies have matured to the point that they have become a lasting influence on the way developers view software and software development—they have become “classics” in their field. Many of the earliest advancements in software design involved the organization of program instructions into modular units. Structured programming, which involves structuring program code into hierarchical structures to avoid jump or “goto” statements, is now reflected in the design of almost all computer languages in common use today. Object-oriented programming, which treats programming as the creation of systems of self-contained “objects” of data and code, is another dominating force in contemporary software design. Unified Modeling Language (UML), a standard notation for modeling systems that is based on object-oriented programming principles, is the culmination of a long evolution in software modeling and appears likely to have a lasting influence on software engineering in the years ahead. Object-oriented programming is intended, among other things, to facilitate code reuse and readability by establishing that certain features in a program (classes, objects, functions, etc.) are generalizations or specializations of other program features. Many modern computer languages, such as Smalltalk, C++, C# (pronounced “C Sharp”), and Java®, explicitly support object-oriented design concepts.
Other software engineering paradigms have focused not only on the organization of program code, but have focused on the semantics (i.e., the formal meaning) of the code itself. Declarative programming is one such paradigm. Declarative program code is written so as to resemble the code's denotational semantics (i.e., the result the code is intended to produce), as opposed to its operational semantics (i.e., the series of actions taken to produce the result). Declarative programming can greatly simplify the task of writing complex programs, because it is usually more intuitive for a programmer to describe a program in terms of what result is desired, rather than in terms of individual steps. Functional programming languages (e.g., ML, Scheme, and Haskell) and logic programming languages (e.g., Prolog, Mercury) are intended exploit the declarative paradigm by defining programs as compositions of functions, as logical statements, or as rules. Declarative programs are often very concise, and for this reason, many of the more recently-developed scripting languages used for rapid application development (e.g., Perl, Python) borrow certain features from functional programming languages (e.g., “foreach” loops, “map” instructions, etc.), even if they are not functional languages in a strict sense. Because well-written declarative programs are usually shorter and more readable than their imperative counterparts, they tend to be less prone to defects (bugs) and more efficient to write.
Despite their expressiveness and convenience, declarative programming languages do not yet enjoy the widespread acceptance that conventional imperative languages do. This is partly due to the fact that historically, implementations of these languages have tended to emphasize theoretical purity and elegance over execution performance, and partly due to the fact that functional and logic programming languages often appear quite foreign to programmers accustomed to more traditional procedural and object-oriented programming languages, many of which were based on earlier procedural languages (e.g., C++ is a superset of C, and Java® and C# are based on C++).
One declarative programming technique that has enjoyed widespread acceptance, however, is in the area of markup languages, such as HyperText Markup Language (HTML) and extensible Markup Language (XML). While markup languages, per se, are not programming languages in the usual sense—they might be better characterized as “data organization languages”—the technique of embedding executable code into markup language documents has become commonplace, especially on the World Wide Web. This may be rightfully be characterized as a form of declarative programming, since the organization (or appearance, in the case of HTML) of such a document itself is expressed directly through the plain text and markup tags making up the markup-language portion(s) of the document and only the embedded executable code portion(s) of the document are made up of imperative commands. This approach has seen wide acceptance in the area of server-side scripting languages for web development, such as PHP, ASP (Active Server Pages), and JSP (Java® Server Pages), which embed executable code within specialized markup tags that, when executed by a web server, are replaced by output generated by the embedded code (if any) before the page is transmitted to the web client.
One technique that allows one to take advantage of many of the benefits of the declarative approach without having to adopt an unconventional programming language is commonly referred to as “code generation.” A “code generator” is a program that takes a high-level description of all or part of a computer program and generates source code from that description. Code generators are thus conceptually similar to compilers or assemblers, but, unlike compilers or assemblers (which produce object code from source code or assembly code), code generators are used to produce source code for subsequent processing by a compiler or interpreter.
Many modern code generators, such as those introduced in conjunction with Microsoft Corporation's Software Factories Initiative, generate source code using markup-language templates, which resembles server-sides scripts written in ASP or PHP in that they embed executable code in special markup language tags. When the template is applied, the special markup language tags are replaced with results computed by the embedded code. The primary difference between these code generator templates and server-side embedded scripts is that the embedded code is embedded into a program source code file rather than a markup language document. The embedded code used by these generator templates will almost always accept some kind of input parameter that will direct the embedded code's modification of the template to form end-product source code. The ultimate effect of these modern template-based code generators is to establish a dichotomy between what might be characterized as “compile-time code” (i.e., code executed during compilation of the software) and “run-time code” (i.e., the final product of compilation). Some programming languages, most notably Forth, have built-in support for code having differing compile-time and run-time semantics, but they have not acquired the degree of acceptance enjoyed by languages in the C/Algol family, for various reasons.
In general, however, existing code generators have tended to be tailored for use by an individual programmer for the purpose of reducing that programmer's need to perform repetitive or tedious tasks, such as building user interface components, lexical analyzers, and parsers, which are very tedious to code by hand. The “Lex” and “Yacc” compiler generation tools originally developed for the UNIX operating system are examples of such code generators. In a large-scale programming project involving a team of participants, however, this kind of small-scale code generation, while helpful, does little to enhance the ability of the participants to function together as an efficient team. Research in the software engineering field has indicated that the ability of the participants in a software engineering project to function together efficiently as a team is often the driving factor in the project's overall productivity.
What is needed, therefore, is a framework for managing and (at least partially) automating the development of large-scale software systems. The present invention provides a solution to this and other problems, and offers other advantages over previous solutions.